﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Dycox.ComponentModel;
using Dycox.Workflows.Manipulation;

namespace Dycox.Workflows
{
    internal class FlowControl
    {
        Workflow _workflow;

        public FlowControl(Workflow workflow)
        {
            _workflow = workflow;
        }

        internal WFProcess CreateProcess()
        {
            return CreateProcess(null);
        }

        internal WFProcess CreateProcess(WFProcess parent)
        {
            WFProcess proc;
            if (parent == null)
                proc = WFProcess.Create(_workflow);
            else
                proc = WFProcess.Create(_workflow, parent);

            proc.Status = WorkflowComponentStatus.Idling;
            WFThread thread = CreateThread(proc);
            thread.ThreadId = WFProcess.MainThreadId;
            return proc;
        }

        internal WFThread CreateChildThread(WFStep step)
        {
            WFThread child = CreateChildThread(step.Thread);
            child.ParentThreadStep = step.Sequence;
            return child;
        }

        internal WFThread CreateChildThread(WFThread parentThread)
        {
            WFThread child = CreateThread(parentThread.Process);
            child.ParentThreadId = parentThread.ThreadId;
            return child;
        }

        private WFThread CreateThread(WFProcess process)
        {
            WFThread thread = process.NewThread();
            thread.Status = WorkflowComponentStatus.Active;
            return thread;
        }

        internal void ValidateEnteringStage(IWorkflowComponent component, Stage stage, IReflectionRoot refObj)
        {
            ConditionTestArguments args = new ConditionTestArguments(component, null, stage, refObj);
            var coll = stage.EnteringCondition;
            if (coll != null && !coll.Test(args))
            {
                string msg = stage.ProhibitMessage;
                if (string.IsNullOrEmpty(msg))
                    msg = string.Format("Cannot enter stage {0}.", stage.DisplayName);
                throw new WorkflowExecutionException(msg);
            }
        }

        internal WFStep CreateStep(WFThread thread, Stage stage, bool activate)
        {
            WFStep step = thread.NewStep(stage);

            if (activate)
                SetStepStatus(step, WorkflowComponentStatus.Active, stage);

            return step;
        }

        internal void ActivateStep(StepMaintainingContext context)
        {
            var step = context.Step;

            if (step.Status == WorkflowComponentStatus.Idling)
            {
                SetStepStatus(step, WorkflowComponentStatus.Active, context.Stage);
                step.IsActive = IsStepActive(step);
            }
        }

        internal void ActivateProcess(WFStep step)
        {
            WFProcess process = step.Thread.Process;

            if (process.Status == WorkflowComponentStatus.Idling || process.Status == WorkflowComponentStatus.Suspending)
            {
                if (process.Status == WorkflowComponentStatus.Idling)
                {
                    if (step.Status != WorkflowComponentStatus.Completed || step.FinishTime == null)
                        throw new WorkflowException("Cannot activate process due to the specified step did not complete.");
                    process.StartTime = step.StartTime;
                }
                
                process.Status = WorkflowComponentStatus.Active;
            }
            else if (process.Status != WorkflowComponentStatus.Active)
                throw new WorkflowException("Cannot activate process when it is in " + process.Status + " status.");
        }

        internal void AbortProcess(WFProcess process, string reason)
        {
            if (IsComponentFinishedCore(process))
                throw new WorkflowException("Cannot abort a finished process.");

            process.Status = WorkflowComponentStatus.Aborted;
            process.FinishTime = WorkflowManager.TimeService.Now;
            process.Properties.AbortReason = reason;

            UpdateStepActivity(process);
        }

        internal void AbortProcess(StepContext step, string reason)
        {

            var pp = step.Process.Properties;
            pp.SuspendPropertyChanged();

            try
            {
                AbortProcess(step.Process, reason);
                pp.AbortStageName = step.Stage.Name;
                pp.AbortStepId = step.Step.ID.ToString();
            }
            finally
            {
                pp.ResumePropertyChanged();
            }
        }

        internal bool IsStepActive(WFStep step)
        {
            bool active = step.Status == WorkflowComponentStatus.Active && !IsSuspendedCore(step) && !IsComponentFinishedCore(step);

            if (active)
            {
                WFThread thread = step.Thread;

                // 不知道当初是怎么考虑的，为什么要所有父线程没有结束时步骤才能算活动。
                // 目前暂时取消这个判断，静观其变。
                // 2012-11-18

                //while (thread != null && active)
                {
                    active = !IsComponentFinishedCore(thread);
                    //thread = thread.ParentThread;
                }

                active = active && !IsComponentFinishedCore(thread.Process);
            }

            return active;
        }

        internal void SetStepStatus(WFStep step, WorkflowComponentStatus newStatus, Stage stage)
        {
            if (stage == null)
                throw new ArgumentNullException("stage");

            var status = step.Status;
            if (newStatus == WorkflowComponentStatus.Idling)
                throw new WorkflowException("Idling is not valid for WFStep.");
            if (newStatus != WorkflowComponentStatus.Aborted && status == WorkflowComponentStatus.Aborted)
                throw new WorkflowException("Cannot change status to non-aborted from which has been aborted.");

            if (status != newStatus)
            {

                switch (newStatus)
                {
                    case WorkflowComponentStatus.Completed:
                        if (status == WorkflowComponentStatus.Active)
                        {
                            step.FinishTime = stage.GetFinishTime(step);
                            if (step.StartTime == null) step.StartTime = stage.GetStartTime(step);
                        }
                        else
                            throw new WorkflowException("Can only complete an non-active step.");
                        break;
                    case WorkflowComponentStatus.Active:
                        if (step.StartTime == null)
                            step.StartTime = stage.GetStartTime(step);
                        if (status == WorkflowComponentStatus.Completed)
                            step.FinishTime = null;
                        break;
                    case WorkflowComponentStatus.Suspending:
                        if (status != WorkflowComponentStatus.Active)
                            throw new WorkflowException("Can only suspend step when it is in Active status.");
                        break;
                    case WorkflowComponentStatus.Aborted:
                        if (status == WorkflowComponentStatus.Completed)
                            throw new WorkflowException("Cannot abort a completed step.");
                        break;
                }
                
                step.Status = newStatus;
            }
        }

        void ThrowIfNotActive(WFStep step)
        {
            if (!IsStepActive(step))
                throw new WorkflowException("Cannot perform this operation when step is not active.");
        }

        void ThrowIfSuspending(IWorkflowComponent component)
        {
            if (component is WFStep && IsSuspendedCore((WFStep)component) ||
                component is WFThread && IsSuspendedCore((WFThread)component) ||
                component is WFProcess && IsSuspendedCore((WFProcess)component))
                throw new WorkflowException("Cannot perform this operation when compoent is suspended.");
        }

        void ThrowIfFinished(IWorkflowComponent component)
        {
            if (IsComponentFinishedCore(component))
                throw new WorkflowException(SR.GetString("component_finished"));
        }

        private bool GetChildThreadsFinished(WFThread thread)
        {
            var threads = thread.GetChildThreads();

            if (threads.Length == 0)
                return true;

            foreach (WFThread child in threads)
            {

                var status = child.Status;
                if (status == WorkflowComponentStatus.Completed || status == WorkflowComponentStatus.Aborted)
                {
                    bool finished = GetChildThreadsFinished(child);
                    if (!finished)
                        return false;
                }
                else
                    return false;
            }

            return true;
        }

        //private Transition PerformTransitionOperations(Stage source, Stage target, WFStep step, WFStep step1)
        //{
        //    Transition t = _workflow.Transitions[source, target];

        //    if (t != null)
        //    {
        //        PerformTransitionOperations(t, step, step1);
        //    }

        //    return t;
        //}

        //private void PerformTransitionOperations(Transition transition, WFStep step, WFStep step1)
        //{
        //    if (transition == null || step == null)
        //        throw new ArgumentNullException();

        //    foreach (var op in transition.Operations)
        //        op.Perform(new TransitionOperationArguments(step, step1));
        //}

        internal MoveResult MoveForward(WorkflowForwardingContext context)
        {

            MoveForwardState state = new MoveForwardState(context);
            MoveResult result = new MoveResult(state.ExecutionContext.Stage, context.Step);
            WFProcess process = context.Process;

            ThreadLock exitSync = state.Exit.ExitSyncLock, exitSync2 = state.NoMoreStep.ExitSyncLock;
            if (exitSync2 != null && exitSync != null && exitSync2 != exitSync)
                throw new WorkflowException("Invalid thread lock configuration, different locks cannot apply on one stage.");

            MoveForward(state, result, false);

            if (!IsComponentFinishedCore(process) && !IsSuspendedCore(process))
            {
                exitSync = exitSync ?? exitSync2;
                if (exitSync != null && !exitSync.ShouldWait(process))
                {
                    List<WFStep> satellites = new List<WFStep>();
                    GetSyncrhonizingThreadSteps(state.ExecutionContext.Thread, exitSync, false, satellites);
                    foreach (WFStep sat in satellites)
                    {
                        exitSync.ExitSynchronizing(sat);
                        MoveForwardState state1 = new MoveForwardState(new WorkflowForwardingContext(context, sat));
                        MoveForward(state1, result, true);
                        if (IsComponentFinishedCore(process) || IsSuspendedCore(process))
                        {
                            break;
                        }
                    }
                }

                if (CanCompleteProcess(context))
                {
                    CompleteProcesCore(process);
                }
            }

            UpdateStepActivity(process);
            UpdateHints(context);

            return result;
        }

        private void UpdateStepActivity(WFProcess process)
        {
            foreach (var stp in process.ThreadsInternal.SelectMany(o => o.StepsInternal))
            {
                stp.IsActive = IsStepActive(stp);
            }
        }

        private List<Transition> GetNextStages(ConditionTestArguments args)
        {
            List<Transition> list = new List<Transition>(5);

            foreach (var tt in _workflow.Transitions.GetTransitionsInternal(args.Stage, null, TransitionTypes.Normal).OrderByDescending(o => o.Rank).ThenBy(o => o.Conditions.Count))
            {
                System.Diagnostics.Debug.Assert(tt.TargetStage.Usage != StageUsage.Placeholder, "The upcoming is not general stage, why?");

                if (tt.UserSelectable && tt.UserSelection.Test(args))
                {
                    list.Clear();
                    list.Add(tt);
                    break;
                }
                if (tt.Conditions.Test(args))
                {
                    list.Add(tt);
                }
            }

            return list;

        }

        private void MoveForward(MoveForwardState state, MoveResult result, bool isSatellite)
        {
            WorkflowForwardingContext context = state.ExecutionContext;

            if (!isSatellite)
            {
                ThrowIfNotActive(context.Step);
                state.Exit.PerformOperations();
                SetStepStatus(context.Step, WorkflowComponentStatus.Completed, context.Stage);
            }

            if (!IsComponentFinishedCore(context.Process) && !IsSuspendedCore(context.Thread))
            {

                if (!state.Exit.ShouldWait)
                {

                    ConditionTestArguments testArgs = state.CreateTestArgs();
                    var nextStages = GetNextStages(testArgs);

                    foreach (var tt in nextStages)
                    {
                        WFThread th;
                        UpcomingState upcoming;
                        Stage nstg = tt.TargetStage;
                        StepMarshalling sm = tt.StepMarshalling;

                        if (!state.HasUpcomings && sm == StepMarshalling.Default)
                            th = context.Thread;
                        else
                        {
                            switch (sm)
                            {
                                case StepMarshalling.Default:
                                case StepMarshalling.NewThreadFromCurrent:
                                    th = CreateChildThread(context.Step);
                                    break;
                                case StepMarshalling.NewThreadFromMain:
                                    th = CreateThread(context.Process);
                                    break;
                                case StepMarshalling.NewThreadFromParent:
                                    th = context.Thread;
                                    if (!th.IsMainThread)
                                    {
                                        th = CreateChildThread(th.ParentThread);
                                        th.ParentThreadStep = context.Thread.ParentThreadId;
                                    }
                                    else
                                        th = CreateChildThread(context.Step);

                                    break;
                                default:
                                    throw new NotSupportedException("Step marshalling is not supported: " + sm);
                            }
                        }

                        ValidateEnteringStage(th, nstg, state.AppObject);

                        WFStep nstp = CreateStep(th, nstg, tt.StepActivation == ActivationMode.Auto);
                        //if (nstg is GeneralStage)
                        //    nstp.Hints = CreateHints((GeneralStage)nstg, state.ExecutionContext.Step, state.UserState, state.AppObject);

                        upcoming = new UpcomingState(state, nstp);

                        state.Upcomings.Add(upcoming);
                        result.AddStep(upcoming);

                        upcoming.Normal.PerformOperations();

                    }

                    if (!state.HasUpcomings)
                    {
                        state.NoMoreStep.PerformOperations();

                        if (CanCompleteThread(context.Thread))
                            CompleteThreadCore(context.Thread);
                    }

                }
            }

        }


        internal bool IsStepFollowedBy(WFStep step, WFStep follower)
        {
            if (step.ProcessId == follower.ProcessId)
            {
                if (step.ThreadId == follower.ThreadId)
                {
                    return follower.Thread.GetPreviousStep(follower) == step;
                }
                else
                {
                    if (follower.Thread.ParentThreadId == step.ThreadId)
                        return follower.Thread.ParentThreadStep == step.Sequence;
                }
            }

            return false;
        }

        private void UpdateHints(WorkflowExecutionContext context)
        {
            foreach (WFStep step in context.Process.ActiveSteps)
            {
                UpdateHints(step, context);
            }
        }

        internal void UpdateHints(WFStep step, WorkflowExecutionContext context)
        {
            Stage gs = this._workflow.Stages[step.StageName];
            WFStep pstep = step.FindNearestStep();
            step.Hints = CreateHints(gs, pstep, context.AppObject);
        }

        //internal MoveResult MoveForwardObsoleted(WFStep step, StageStateContract userState, IReflectionRoot refobject)
        //{
        //    WFProcess process = step.Thread.Process;

        //    Stage stage= _workflow.Stages[step.StageName];
        //    MoveResult result = new MoveResult(stage, step);
        //    Transition exitTran;

        //    exitTran = _workflow.Transitions.GetTransition(stage, TransitionTypes.ExitStage);
        //    if (exitTran != null) PerformTransitionOperations(exitTran, step, null);

        //    if (!IsSuspendedCore(step))
        //    {

        //        ThreadLockOperation wait = null;

        //        if (exitTran != null)
        //            wait = exitTran.Operations.OfType<ThreadLockOperation>().LastOrDefault();

        //        List<WFStep> list = new List<WFStep>();

        //        if (wait != null && !(wait is ThreadLockSetOperation))
        //        {

        //            ThreadLock tlock;
        //            tlock = wait.GetLock(process);

        //            System.Diagnostics.Debug.Assert(!step.Thread.Properties.IsLocked, "线程不应该处于锁定状态。");

        //            if (!tlock.ShouldWait(process))
        //            {
        //                GetSyncrhonizingThreadSteps(step.Thread, tlock, false, list);

        //                foreach (WFStep st in list)
        //                {
        //                    System.Diagnostics.Debug.Assert(!st.Thread.Properties.IsLocked, "线程此时不应处于锁定状态。");
        //                    tlock.ExitSynchronizing(st);
        //                }

        //                list.Insert(0, step);
        //            }
        //        }
        //        else
        //            list.Add(step);

        //        if (list.Count > 0)
        //        {
        //            foreach (WFStep stp in list)
        //            {
        //                Stage stg = _workflow.Stages[stp.StageName];
        //                StageStateContract ssc = stp.GetUserState(stg);
        //                ConditionTestArguments args = new ConditionTestArguments(stp, ssc, stg, refobject);
        //                GeneralStage[] nextStages;

        //                nextStages = GetNextStages(args);

        //                if (nextStages.Length > 0)
        //                {
        //                    bool newThread = false;

        //                    foreach (GeneralStage nstg in nextStages)
        //                    {
        //                        WFThread t;
        //                        WFStep nstp;

        //                        if (newThread) t = CreateChildThread(stp);
        //                        else t = stp.Thread;

        //                        ValidateEnteringStage(t, nstg, refobject);

        //                        nstp = CreateStep(t, nstg);

        //                        nstp.Hints = CreateHints(nstg, stp, ssc, refobject);

        //                        PerformTransitionOperations(stg, nstg, stp, nstp);
                                
        //                        newThread = true;
        //                        result.AddStep(nstp, stp == step);
        //                    }
        //                }
        //                else
        //                {
        //                    // 已经没有后续步骤，尝试完成此线程。

        //                    Transition t = _workflow.Transitions.GetTransition(stg, TransitionTypes.NoMoreStep);
        //                    if (t != null)
        //                        PerformTransitionOperations(t, stp, null);

        //                    if (CanFinishThread(step.Thread))
        //                    {
        //                        CompleteThreadCore(step.Thread);

        //                        if (CanCompleteProcess(process))
        //                        {
        //                            CompleteProcesCore(process);
        //                            result.ProcessCompleted = true;
        //                        }
        //                    }
        //                }
        //            }
        //        }
        //    }

        //    return result;
        //}

        private MessageSet CreateHints(Stage stage, WFStep lastStep, IReflectionRoot refobj)
        {
            if (stage.HasHints)
            {
                //Stage lastStage;
                //if (lastStep != null) lastStage = _workflow.Stages[lastStep];
                //else lastStage = null;

                //PopulateHintEventArgs e = new PopulateHintEventArgs(lastStage, lastStep, stage, refobj);
                //_workflow.OnPopulateHint(e);
                //if (e.Hint.Count > 0)
                //    step.Hints = e.Hint;

                MessageSet hints = new MessageSet();
                StageOperationContract userState = lastStep.GetContract();

                ConditionTestArguments args = new ConditionTestArguments(lastStep, userState, stage, refobj);
                foreach (Hint hint in stage.Hints)
                {
                    bool ok = true;
                    if (hint.Conditions.Count > 0)
                    {
                        ok = hint.Conditions.Test(args);
                    }
                    if (ok)
                    {
                        string text = hint.Text;
                        if (string.IsNullOrEmpty(text))
                        {
                            PopulateHintEventArgs e = new PopulateHintEventArgs(hints, _workflow.Stages[lastStep], lastStep, stage, refobj);
                            _workflow.OnPopulateHint(e);
                        }
                        else
                        {
                            if (text.IndexOf('{') >= 0)
                                text = Dycox.Utils.DataBinderUtil.EvalTemplate(text, args);

                            var entry = hints.Add(text, hint.Kind);
                            entry.ShortText = hint.ShortText;
                        }
                    }
                }

                return hints;
            }
            else
                return null;
        }



        private bool CanCompleteProcess(WorkflowExecutionContext context)
        {
            WFProcess process = context.Process;

            if (IsComponentFinishedCore(process))
                return false;
            
            bool ok = !process.ThreadsInternal.Any(o => !IsComponentFinishedCore(o));

            if (ok && _workflow.CompleteConditions.Count > 0)
            {
                ConditionTestArguments args = new ConditionTestArguments(context.Step, context.UserState, context.Stage, context.AppObject);
                ok = _workflow.CompleteConditions.Test(args);
            }

            return ok;
        }

        private void CompleteProcesCore(WFProcess process)
        {
            process.Status = WorkflowComponentStatus.Completed;
            process.FinishTime = WorkflowManager.TimeService.Now;
        }

        /// <summary>
        /// 完成指定的线程。注意，此方法不检测线程是否允许完成，因此在调用前先调用<see cref="CanCompleteThread"/>方法以确保线程可以完成。
        /// </summary>
        /// <param name="thread"></param>
        private void CompleteThreadCore(WFThread thread)
        {
            //if (!CanFinishThread(thread))
            //    throw new WorkflowException("Thread cannot complete yet.");

            thread.Status = WorkflowComponentStatus.Completed;

            if (!thread.IsMainThread)
            {
                thread = thread.ParentThread;
                if (CanCompleteThread(thread))
                    CompleteThreadCore(thread);
            }
        }

        //private int CreateNextSteps(ConditionTestArguments args, List<WFStep> stepList)
        //{
        //    Stage[] nextStages;
        //    int count;
        //    WFStep step = args.Step;

        //    nextStages = GetNextStages(args);
        //    count = nextStages.Length;

        //    if (count > 0)
        //    {
        //        if (count == 1)
        //        {
        //            stepList.Add(CreateStep(step.Thread, nextStages[0]));
        //        }
        //        else
        //        {
        //            WFThread currentThread = null;
        //            foreach (Stage s in nextStages)
        //            {
        //                WFStep nextStep;
        //                WFThread thread;

        //                if (s.ThreadAnchor == ThreadAnchorType.None)
        //                    thread = CreateChildThread(step);
        //                else
        //                {
        //                    if (currentThread != null)
        //                        throw new WorkflowException("Only one stage can be the anchor while in parallel mode.");
        //                    thread = currentThread = step.Thread;
        //                    threadList.Add(thread);
        //                }
        //                nextStep = CreateStep(thread, s);
        //                stepList.Add(nextStep);
        //            }
        //        }
        //    }


        //    return count;
        //}

        internal WFStep GetNearestCompletedStep(WFStep step, string stageName)
        {
            WFThread thread = step.Thread;
            WFStep value = thread.GetPreviousStep(step);

            while (true)
            {
                if (value == null)
                {
                    if (thread.IsMainThread) break;

                    int? seq = thread.ParentThreadStep;

                    thread = thread.Process.GetThread(thread.ParentThreadId);
                    if (thread == null) break;

                    if (seq == null) value = thread.LastStep;
                    else value = thread.GetStep(seq.Value);

                    if (value == null)
                        throw new WorkflowException("Stage name " + stageName + " cannot be retreived due to workflow chain broken.\nStep ID: " + ((IWorkflowComponent)step).ID);
                }
                
                if (value.Status == WorkflowComponentStatus.Completed && (stageName == null || value.StageName == stageName))
                    break;
                else
                    value = thread.GetPreviousStep(value);
            }

            return value;
        }

        internal static bool IsSuspendedCore(WFProcess process)
        {
            return process.Status == WorkflowComponentStatus.Suspending;
        }

        /// <summary>
        /// 返回一个值，指示指定的线程是否被挂起。
        /// </summary>
        /// <param name="thread"></param>
        /// <returns>
        /// 当线程处于挂起状态，或其父线程处于挂起状态，或者所属的进程处于挂起状态，此线程都会被视为挂起。
        /// </returns>
        internal static bool IsSuspendedCore(WFThread thread)
        {
            if (thread == null)
                throw new ArgumentNullException("thread");

            //WFThread t = thread;

            //while (t != null)
            //{
            //    if (t.Status == WorkflowComponentStatus.Suspending)
            //        return true;
            //    t = t.ParentThread;
            //}

            if (thread.Status == WorkflowComponentStatus.Suspending)
                return true;

            if (!thread.IsMainThread)
                return thread.ParentThread.IsSuspended;
            else
                return IsSuspendedCore(thread.Process);
        }

        /// <summary>
        /// 返回一个值，指示指定的步骤<see cref="WFStep"/>是否被挂起。
        /// </summary>
        /// <param name="step"></param>
        /// <returns></returns>
        /// <remarks>
        /// 当一个步骤处于挂起状态，或其所在的线程被挂起，此步骤被会被视为已挂起。
        /// </remarks>
        internal static bool IsSuspendedCore(WFStep step)
        {
            if (step == null)
                throw new ArgumentNullException("step");

            if (step.Status == WorkflowComponentStatus.Suspending)
                return true;

            return step.Thread.IsSuspended;
        }

        /// <summary>
        /// 返回一个值，指示线程是否处于需要完成的临界状态。
        /// </summary>
        /// <param name="thread"></param>
        /// <returns></returns>
        /// <remarks>
        /// 当线程处于临界状态时，表示线程需要被结束，临界状态的线程应具备以下几个条件。
        /// <para>1、线程上至少有1个步骤。</para>
        /// <para>2、线程没有被挂起，也没有结束。</para>
        /// <para>3、线程已经没有活动的步骤了。</para>
        /// <para>4、线程上没有步骤处于挂起状态。</para>
        /// </remarks>
        internal static bool IsCriticalCore(WFThread thread)
        {
            if (thread == null)
                throw new ArgumentNullException("thread");

            return
                thread.StepsInternal.Count > 0 &&
                !IsComponentFinishedCore(thread) &&
                !IsSuspendedCore(thread) &&
                thread.ActiveStep == null &&
                !thread.StepsInternal.Any(o => o.Status == WorkflowComponentStatus.Suspending) &&
                !ThreadLock.IsThreadSychronizing(thread);
        }

        internal static bool IsComponentFinishedCore(IWorkflowComponent component)
        {
            WorkflowComponentStatus status = component.Status;
            return status == WorkflowComponentStatus.Completed || status == WorkflowComponentStatus.Aborted;
        }

        /// <summary>
        /// 返回一个值，指示是否允许完成一个线程。
        /// </summary>
        /// <param name="thread"></param>
        /// <returns></returns>
        /// <remarks>
        /// 要完成一个线程必须具备以下几个条件：
        /// <para>1、线程处于临界状态，参考<see cref="P:IsCritical"/>。</para>
        /// <para>2、所有后代线程已经结束，即处于<see cref="WorkflowComponentStatus.Completed"/>或<see cref="WorkflowComponentStatus.Aborted"/>状态。</para>
        /// </remarks>
        internal static bool CanCompleteThread(WFThread thread)
        {
            if (thread == null)
                throw new ArgumentNullException("thread");

            if (IsCriticalCore(thread))
            {
                Func<WFThread, bool> func = (t) =>
                    {
                        return IsComponentFinishedCore(t);
                    };

                return WalkDescedentThreads(thread, false, func);
            }
            else
                return false;
        }

        internal static bool WalkDescedentThreads(WFThread thread, bool includeThisOne, Func<WFThread, bool> callback)
        {
            if (callback == null)
                throw new ArgumentNullException("callback");
            if (thread == null)
                throw new ArgumentNullException("thread");

            ICollection<WFThread> coll;

            if (includeThisOne) coll = new WFThread[] { thread };
            else coll = thread.GetChildThreads();

            return WalkChildThreads(coll, callback);
        }

        private static bool WalkChildThreads(ICollection<WFThread> coll, Func<WFThread, bool> callback)
        {
            foreach (WFThread t in coll)
            {
                bool ok = callback(t);

                if (!ok) return false;

                WalkChildThreads(t.GetChildThreads(), callback);
            }

            return true;
        }

        private void GetSyncrhonizingThreadSteps(WFThread sampleThread, ThreadLock tlock, bool includeSample, List<WFStep> list)
        {
            foreach (WFThread t in sampleThread.Process.ThreadsInternal)
            {
                if (ThreadLock.IsThreadSychronizing(t, tlock) && (t != sampleThread || includeSample))
                {
                    WFStep step = t.GetStep(t.Properties.LockingStep);
                    if (step == null)
                        throw new InvalidOperationException(string.Format("The locking step {0} on thread {1} not found.", t.Properties.LockingStep, t.ID));

                    list.Add(step);
                }
            }
        }

        internal void SuspendThread(WFThread thread)
        {
            ThrowIfFinished(thread);

            var pp = thread.Properties;
            var ps = thread.Status;

            // TODO: 挂起线程的处理
            // 如果在Exit中调用了SuspendProcess或SuspendThread，就会使后面的步骤无法创建。
            // 因此需要记录下挂起进程的步骤，在Resume的时候重新执行后半部分。

            if (ps != WorkflowComponentStatus.Suspending)
            {
                thread.Status = WorkflowComponentStatus.Suspending;
                pp.SuspendingStatus = ps;
                UpdateStepActivity(thread.Process);

            }
            else
                throw new WorkflowException(SR.GetString("component_suspended"));
        }

        internal void SuspendProcess(WFProcess process, ProcessSuspendingReasons reason)
        {
            ThrowIfFinished(process);

            var pp = process.Properties;
            var ps = process.Status;

            // TODO: 挂起进程的处理
            // 如果在Exit中调用了SuspendProcess或SuspendThread，就会使后面的步骤无法创建。
            // 因此需要记录下挂起进程的步骤，在Resume的时候重新执行后半部分。

            if (ps != WorkflowComponentStatus.Suspending ||
                (pp.SuspendingReasons & reason) == ProcessSuspendingReasons.None)
            {
                pp.SuspendingReasons |= reason;
                if (ps != WorkflowComponentStatus.Suspending)
                {
                    process.Status = WorkflowComponentStatus.Suspending;
                    pp.SuspendingStatus = ps;
                    UpdateStepActivity(process);
                }

            }
            else
                throw new WorkflowException(SR.GetString("component_suspended"));

        }

        internal void ResumeProcess(WFProcess process, ProcessSuspendingReasons reason)
        {
            
            if (process.Status == WorkflowComponentStatus.Suspending)
            {
                var pp = process.Properties;
                var r = pp.SuspendingReasons;
                if ((r & reason) != reason)
                    throw new WorkflowException(SR.GetString("component_not_suspending"));

                r &= (~reason);

                pp.SuspendingReasons = r;
                if (r == ProcessSuspendingReasons.None)
                {
                    process.Status = pp.SuspendingStatus;
                    UpdateStepActivity(process);
                }
            }
            else
                throw new WorkflowException(SR.GetString("component_not_suspending"));
        }

        internal void ResumeThread(WFThread thread)
        {

            if (IsComponentSuspended(thread))
            {
                var pp = thread.Properties;

                thread.Status = pp.SuspendingStatus;
                UpdateStepActivity(thread.Process);
            }
            else
                throw new WorkflowException(SR.GetString("component_not_suspending"));
        }

        internal bool IsComponentSuspended(IWorkflowComponent component)
        {
            return component.Status == WorkflowComponentStatus.Suspending;
        }

        internal bool IsProcessSuspended(WFProcess process, ProcessSuspendingReasons reasons)
        {
            if (IsComponentSuspended(process))
                return (process.Properties.SuspendingReasons & reasons) == reasons;
            else
                return false;
        }

        //{
        //    if (.Status == WorkflowComponentStatus.Suspending)
        //    {
        //        if (!forced)
        //        {
        //            ProcessSuspendingReasons r0 = thread.SuspendReasons;
        //            if ((r0 & reason) == reason)
        //            {
        //                r0 = r0 & (~reason);
        //                thread.SuspendReasons = r0;
        //            }
        //        }
        //        else
        //            thread.SuspendReasons = ProcessSuspendingReasons.None;

        //        if (thread.SuspendReasons == ProcessSuspendingReasons.None)
        //        {
        //            thread.Status = WorkflowComponentStatus.Active;
        //            return true;
        //        }
        //        else
        //            return false;
        //    }
        //    else
        //        return true;

        //}
    }
}
